/*================================================================
*   Copyright (C) 2020 belonging to YangZhengQing. All rights reserved.
*   
*   文件名称：fifo.c
*   创 建 者：YangZhengQing
*   创建日期：2020年03月10日
*   描    述：FIFO实现全双工，一个程序运行两次就是两个进程，通过命令行控制
*
*================================================================*/


//#include "fifo.h"

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <sys/epoll.h>
#include <sys/wait.h>

int					g_stop = 0;

void sig_pipe(int signum)
{
	if(SIGPIPE == signum)
	{
		printf("get pipe broken signal and let programe exit\n");
		g_stop = 1;
	}
}


int main(int argc, char **argv)
{
	int					fdr_fifo;
	int					fdw_fifo;
	char				buf[1024];

	struct epoll_event  ctl_event;
	struct epoll_event  rv_event[2];

	int					rv = -1;
	int					rvctl = -1;
	int					rvwait = -1;
	int					epollfd;
	int					i;

	//判断文件是否存在
	if(access(".fifo1",F_OK) < 0)
	{
		printf(".fifo1不存在，将要被创建!\n");
		mkfifo(".fifo1",0777);
	}
	if(access(".fifo2",F_OK) < 0)
	{
		printf(".fifo2不存在，将要被创建!\n");
		mkfifo(".fifo2",0777);
	}

	signal(SIGPIPE, sig_pipe);

	//打开管道，通过终端命令行控制应该打开哪一个管道
	if(0 == atoi(argv[1]))
	{
		
		fdr_fifo = open(".fifo1",O_RDONLY);//另外一个进程要首先以写模式打开FIFO1，否则两个进程都会被阻塞
		if(fdr_fifo < 0)
		{
			printf("fifo1读模式打开失败:%s\n",strerror(errno));
			exit(0);//程序退出
		}
		
		printf("成功打开fifo1的读模式!\n");

		fdw_fifo = open(".fifo2",O_WRONLY);
		if(fdw_fifo < 0)
		{
			 printf("fifo2写模式打开失败:%s\n",strerror(errno));
			 exit(0);//程序退出
		}
		
		printf("成功打开fifo2的写模式!\n");
	}
	else
	{
		
		fdw_fifo = open(".fifo1",O_WRONLY);//另外一个进程要首先以写模式打开FIFO1，否则两个进程都会被阻塞
		if(fdw_fifo < 0)
		{
			printf("fifo1写模式打开失败:%s\n",strerror(errno));
			exit(0);//程序退出
		}

		printf("成功打开fifo1的写模式!\n");

		fdr_fifo = open(".fifo2",O_RDONLY);
		if(fdr_fifo < 0)
		{
			 printf("fifo2读模式打开失败:%s\n",strerror(errno));
			 exit(0);//程序退出
		}

		printf("成功打开fifo2的读模式!\n");
		
	}


	epollfd = epoll_create(2);
	if(epollfd < 0)
	{
		printf("epoll create error:%s\n",strerror(errno));
		goto Cleanup;
	}

	//初始化注册标准输入
	ctl_event.events = EPOLLIN;//设置为读
	ctl_event.data.fd = STDIN_FILENO;
	rvctl = epoll_ctl(epollfd,EPOLL_CTL_ADD,STDIN_FILENO,&ctl_event);
	if(rvctl < 0)
	{
		printf("ctl stdin 失败:%s\n",strerror(errno));
		close(epollfd);
		goto Cleanup;
	}
	//初始化注册fdr_fifo
	ctl_event.events = EPOLLIN;//设置为读
	ctl_event.data.fd = fdr_fifo;
	rvctl = epoll_ctl(epollfd,EPOLL_CTL_ADD,fdr_fifo,&ctl_event);
	if(rvctl < 0)
	{
		printf("ctl fdr_fifo 失败:%s\n",strerror(errno));
		close(epollfd);
		goto Cleanup;
	}
	

	//epoll处理两个文件描述符：标准输入和fdr_fifo
	while(!g_stop)
	{
		printf("开始监听文件描述符...\n");
		rvwait = epoll_wait(epollfd,rv_event,2,-1);//-1:永远阻塞直到有文件描述被触发时返回
		if(rvwait < 0)
		{
			printf("epoll_wait error:%s\n",strerror(errno));
			close(epollfd);
			goto Cleanup;
		}
		else if(rvwait == 0)
		{
			printf("wait timeout!\n");
			continue;
		}

		printf("监听返回!rvwait=%d\n",rvwait);
		if(rvwait > 0)
		{

			for(i = 0; i < rvwait; i++)
			{
				if(rv_event[i].data.fd == STDIN_FILENO)//标准输入有数据
				{
					memset(buf, 0, sizeof(buf));
					fgets(buf, sizeof(buf), stdin);
					rv = write(fdw_fifo, buf, strlen(buf));
					if(rv < 0)
					{
						printf("向管道写入数据错误:%s\n",strerror(errno));
						close(rv_event[i].data.fd);
						goto Cleanup;
					}

				}
				else //fdr_fifo文件描述可读
				{

					
					printf("文件描述[%d]状态是[%d]!\n",rv_event[i].data.fd,rv_event[i].events);
					if(rv_event[i].events & EPOLLIN)
					{
						printf("进入if...\n");
						memset(buf, 0, sizeof(buf));
						rv = read(fdr_fifo, buf, sizeof(buf));
						if( rv < 0)
						{
							printf("从fdr_fifo读数据失败: %s\n", strerror(errno));
							close(rv_event[i].data.fd);
							goto Cleanup;
						}
						else if( 0 == rv ) /* 如果从管道上读到字节数为0，说明管道的写端已关闭 */
						{
							printf("管道另一端已关闭\n");
							close(rv_event[i].data.fd);
							goto Cleanup;

						}
						printf("从管道读到[%d]字节数据:%s",rv, buf);

					
					}
					else if(rv_event[i].events & EPOLLHUP)//文件描述被挂断
					{

					   printf("管道另一端已关闭\n");
					   close(rv_event[i].data.fd);
					   goto Cleanup;
					}
				}

			}
		
		}
	}
Cleanup:
	exit(0);


}

